moba.creeps = {}
moba.creeps_projectiles = {}

moba.creeps_lanenodes = {}
moba.creeps_lanepaths = {{},{}}
moba.creeps_voidpath = {path = {}, node = 0}
moba.creeps_freetile = function(x,y,creepid)
	local exclude_target = 0
	local exclude_type = ""
	if creepid then exclude_type, exclude_target = moba.creeps[creepid].target_t, moba.creeps[creepid].target end
	
	for k,id in pairs(player(0,"tableliving")) do
		if exclude_type ~= "players" or exclude_target ~= k then
			local px, py = player(id,"x"), player(id,"y")
			if x >= math.floor((px-12)/32) and y >= math.floor((py-12)/32)
			and x <= math.floor((px+12)/32) and y <= math.floor((py+12)/32) then
				return
			end
		end
	end
	for k,building in pairs(moba.buildings) do
		if exclude_type ~= "buildings" or exclude_target ~= k then
			local floor = math.floor
			local w = floor(moba.config.buildings[building.type].size.w/2)
			local h = floor(moba.config.buildings[building.type].size.h/2)
			if not( x >= floor((building.x+w)/32) or x < floor((building.x-w)/32)
			or y >= floor((building.y+h)/32) or y < floor(building.y-h)/32) then
				return
			end
		end
	end
	return true
end
for _,e in pairs(entitylist()) do
	local lane,node = string.match(string.lower(entity(e.x,e.y,"name") or ""),"^mobalane(%d+)-(%d+)$")
	lane,node = tonumber(lane), tonumber(node)
	if lane and node then
		local lanenodes = moba.creeps_lanenodes
		local lanepaths = moba.creeps_lanepaths
		if not lanenodes[lane] then
			lanenodes[lane] = {}
			lanepaths[1][lane] = {}
			lanepaths[2][lane] = {}
		end
		lanenodes[lane][node] = {x=e.x,y=e.y}
	end
end
do
	local lanenodes = moba.creeps_lanenodes
	local lanepaths = moba.creeps_lanepaths
	for lane = 1, #lanenodes do
		--first calculate dire path
		for node = 2, #lanenodes[lane] do
			local pnode = lanenodes[lane][node-1]
			local cnode = lanenodes[lane][node]
			local p = astar(pnode.x,pnode.y,cnode.x,cnode.y,moba.creeps_freetile)
			for i=1, #p do
				lanepaths[1][lane][#lanepaths[1][lane]+1] = p[i]
			end
		end
		for i=1, #lanepaths[1][lane] do
			lanepaths[2][lane][i] = lanepaths[1][lane][#lanepaths[1][lane]-i+1]
		end
	end
end

function moba.creeps_spawn(x,y,team,lane,typ)
	local creeps = moba.creeps
	for i=1,#creeps+1 do
		if not creeps[i] then
			creeps[i] = {
				hp = moba.config.creeps[typ].hp,
				x = x,
				y = y,
				team = team,
				type = typ,
				lane = lane,
				image = image(moba.config.creeps[typ].image:format(team),0,1,3),
				target = nil, target_t = nil,
				paths = {},
				canattack = true
			}
			local sz = moba.config.creeps[typ].size
			imagepos(creeps[i].image,x+sz.w/2,y+sz.h/2,0)
			return i
		end
	end
end

function moba.creeps_allowattack(i)
	moba.creeps[tonumber(i)].canattack = true
end
function moba.creeps_destroy(i)
	freeimage(moba.creeps[i].image)
	moba.creeps[tonumber(i)] = nil
end
function moba.creeps_kill(cid,src)
	src = tonumber(src)
	if src then
		moba.addexp(src,moba.config.creeps[moba.creeps[cid].type].xp)
		moba.addgold(src,moba.config.creeps[moba.creeps[cid].type].gold)
	end
	moba.creeps_destroy(cid)
end
function moba.creeps_destroyprojectile(i)
	freeimage(moba.creeps_projectiles[i].image)
	moba.creeps_projectiles[i] = nil
end
function moba.creeps_setdest(c,i,x,y,ft)
	local creep = moba.creeps[c]
	local p = astar(math.floor(creep.x/32),math.floor(creep.y/32),x,y,ft or moba.creeps_freetile)
	if p then
		return moba.creeps_setpath(c,i,p)
	end
end
function moba.creeps_setpath(c,i,p)
	local creep = moba.creeps[c]
	if i > #creep.paths+1 then
		for j=i-1,#creep.paths+1,-1 do
			creep.paths[j] = creep.paths[j] or moba.creeps_voidpath
		end
	end
	creep.paths[i] = {path = p, node = 1}
	return creep.paths[i]
end

function moba.creeps_findnewtarget(i)
	local creep = moba.creeps[i]
	local inradius = moba.pathfinding_inradius
	for k,id in pairs(player(0,"team".. 3-creep.team.."living")) do
		if inradius(creep.x,creep.y,player(id,"x"),player(id,"y"),moba.config.creeps[creep.type].los) then
			return "players", id
		end
	end
	for k,c in pairs(moba.creeps) do
		if c.team == 3-creep.team then
			if inradius(creep.x,creep.y,c.x,c.y,moba.creeps[creep.type].los) then
				return "creeps", k
			end
		end
	end
end

function moba.creeps_hunt(i)
	local creep = moba.creeps[i]
	creep.paths[3], creep.paths[4] = moba.creeps_voidpath, moba.creeps_voidpath --no target, no attack path
	creep.target_t, creep.target = moba.creeps_findnewtarget(i) --no target, look for a new one
end

addhook("startround","moba.creeps_reset")
function moba.creeps_reset()
	for k,v in pairs(moba.creeps) do
		moba.creeps[k] = nil
	end
end

--[[CREEP PATHS:
	[1] lane path
		the path that the creep takes to reach the last lane node
	[2] aux lane path
		the path that the creep takes when it finds an obstacle in the lane path
	[3] attack path
		the path that the creep takes to reach a target
	[4] aux attack path
		the path that the creep takes when it finds an obstacle in the attack path
	path priority is given in ascending order, ie paths[4] > paths[1]
]]
addhook("always","moba.creeps_always")
function moba.creeps_always()
	for k,creep in pairs(moba.creeps) do
		local canmove = true
		local hunt = true
		local goback = false
		if creep.target then
			if moba.id_exists(creep.target_t,creep.target) and moba.id_field(creep.target_t,creep.target,"hp") > 0 then
				local tx,ty = moba.id_field(creep.target_t,creep.target,"x"), moba.id_field(creep.target_t,creep.target,"y")
				local inradius = moba.pathfinding_inradius
				if inradius(creep.x,creep.y,tx,ty,moba.config.creeps[creep.type].los) then
					--target is in los range
					hunt = false
					goback = false
					if inradius(creep.x,creep.y,tx,ty,moba.config.creeps[creep.type].range) then
						--target is in attack range
						canmove = false
						if creep.canattack then
							local cfg = moba.config.creeps[creep.type]
							creep.canattack = false
							timer(cfg.aspd, "moba.creeps_allowattack", tostring(k))
							local name = cfg.name:format(strings["team"..creep.team])
							if cfg.projectiles then
								moba.projectiles_spawn(creep.x,creep.y,"creeps",k,name,
									creep.target_t,creep.target,creep.team,
									cfg.projectiles.damage,cfg.projectiles.speed,cfg.projectiles.image:format(creep.team)
								)
							else
								if moba.physdmgplayer(creep.target,name,cfg.damage,cfg.damage_m,0) then
									--target has been killed
									hunt = true
									goback = true
								end
							end
						end
					else
						if creep.paths[3] == moba.creeps_voidpath or not creep.paths[3] then
							moba.creeps_setdest(k,3,math.floor(tx/32),math.floor(ty/32),function(x,y)
								return moba.creeps_freetile(x,y,k)
							end)
						else
							local lastnode = creep.paths[3].path[#creep.paths[3].path]
							if lastnode.x ~= tx or lastnode.y~=ty then
								local buff = astar(lastnode.x,lastnode.y, math.floor(tx/32), math.floor(ty/32), function(x,y)
									return moba.creeps_freetile(x,y,k)
								end)
							end
						end
					end
				else
					--target has fled
					hunt = true
					goback = true
				end
			else
				--target has died or it left the game
				hunt = true
				goback = true
			end
		end
		if hunt then moba.creeps_hunt(k) end
		if goback then
			local destnode = creep.paths[1].path[creep.paths[1].node]
			moba.creeps_setdest(k,2,destnode.x,destnode.y)
		end
		if canmove then
			local pathindex = #creep.paths
			local destpath = creep.paths[pathindex]
			while (destpath == moba.creeps_voidpath or not destpath) and pathindex > 0 do
				pathindex = pathindex-1
				destpath = creep.paths[pathindex]
			end
			if destpath == moba.creeps_voidpath or not destpath then
				--still nowhere to go? stand
			elseif not moba.creeps_freetile(destpath.path[destpath.node].x,destpath.path[destpath.node].y,k) then
				local prev = creep.paths[pathindex+pathindex%2-1]
				for i=prev.node, #prev.path do
					if moba.creeps_freetile(prev.path[i].x,prev.path[i].y) then
						moba.creeps_setdest(k,pathindex+pathindex%2,prev.path[i].x,prev.path[i].y)
						prev.node = i
						break
					end
				end
			else
				local xd = destpath.path[destpath.node].x*32+16-creep.x
				local yd = destpath.path[destpath.node].y*32+16-creep.y
				if xd==0 and yd==0 then
					destpath.node = destpath.node +1
					if destpath.node > #destpath.path then
						if pathindex%2 == 1 and pathindex > 1 then
							--attack paths usually don't lead back to the lane path so we re-trace the lane aux path
							local lanepath = creep.paths[1]
							for i=lanepath.node, #lanepath.path do
								if moba.creeps_freetile(lanepath.path[i].x,lanepath.path[i].y) then
									moba.creeps_setdest(k,2,lanepath.path[i].x,lanepath.path[i].y)
									lanepath.node = i
									break
								end
							end
						end
						creep.paths[pathindex] = moba.creeps_voidpath --voidpath avoids index skipping
					end
				else
					local abs = math.abs
					local absmin = function(x,y)
						if abs(x) >= abs(y) then return y end
						return x
					end
					local d = (xd*xd+yd*yd)^0.5
					local xf = xd/d*moba.id_config("creeps",creep.type).speed
					local yf = yd/d*moba.id_config("creeps",creep.type).speed
					creep.x = creep.x + absmin(xf,xd)
					creep.y = creep.y + absmin(yf,yd)
					imagepos(creep.image,creep.x,creep.y,math.deg(math.atan2(yd,xd))+90)
				end
			end
		end
	end
end